package jmine.tec.rpc.common.serialization.json;

import jmine.tec.json.JSON;
import jmine.tec.json.JSONObject;

/**
 * Classe base para conversores de objetos JSON para Java
 * 
 * @author lundberg
 * @param <T> tipo de objeto extraído
 */
public abstract class AbstractJSONObjectConverter<T> implements JSONConverterStrategy {

    private static final String TYPE = "&type";

    /**
     * Construtor
     */
    public AbstractJSONObjectConverter() {
        super();
    }

    /**
     * {@inheritDoc}
     */
    public boolean matchesJSON(JSON json) {
        if (!(json instanceof JSONObject)) {
            return false;
        }
        JSONObject object = (JSONObject) json;
        return this.objectType().equals(object.getProperty(TYPE));
    }

    /**
     * {@inheritDoc}
     */
    public final JSON toJSON(JSONConversionContext context, Object object) {
        JSONObject json = new JSONObject();
        context.storeReference(json, object);
        json.setProperty(TYPE, this.objectType());
        this.fillJSON(context, object, json);
        return json;
    }

    /**
     * Preenche o objeto JSON com os dados específicos da representação
     * 
     * @param context context
     * @param object object
     * @param json json
     */
    protected abstract void fillJSON(JSONConversionContext context, Object object, JSONObject json);

    /**
     * {@inheritDoc}
     */
    public final Object toJava(JSONConversionContext context, JSON json) {
        T object = this.instantiate(context, json);
        context.storeReference(json, object);
        this.fillObject(context, json, object);
        return object;
    }

    /**
     * Instancia o objeto Java que deve ser extraído do JSON
     * 
     * @param context context
     * @param json json
     * @return object
     */
    protected abstract T instantiate(JSONConversionContext context, JSON json);

    /**
     * Preenche os atributos do objeto Java extraído do JSON
     * 
     * @param context context
     * @param json json
     * @param object object
     */
    protected abstract void fillObject(JSONConversionContext context, JSON json, T object);

    /**
     * Retorna o objeto JSON que identifica que este é o conversor
     * 
     * @return JSON
     */
    protected abstract JSON objectType();

    /**
     * Verifica se a propriedade corresponde a um atributo especial.
     * 
     * @param key key
     * @return boolean
     */
    protected boolean isSpecialProperty(String key) {
        return key.startsWith("&");
    }
}